package com.spic.system.activiti.service.impl;

import com.spic.common.core.utils.SecurityUtils;
import com.spic.common.core.web.page.PageDomain;
import com.spic.common.core.web.page.TableSupport;
import com.spic.common.security.service.TokenService;
import com.spic.common.security.utils.cache.UserCacheUtils;
import com.spic.system.activiti.domain.BusinessStatus;
import com.spic.system.activiti.domain.dto.TaskFinshDTO;
import com.spic.system.activiti.enums.BusinessStatusEnum;
import com.spic.system.activiti.image.CustomProcessDiagramGenerator;
import com.spic.system.activiti.service.IBusinessStatusService;
import com.spic.system.activiti.service.IProcessInstanceService;
import com.spic.system.activiti.utils.DateUtils;
import com.spic.system.api.domain.SysUser;
import com.spic.system.api.model.LoginUser;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Transactional
public class ProcessInstanceServiceImpl extends ActivitiService implements IProcessInstanceService
{
    @Autowired
    private IBusinessStatusService businessStatusService;
    @Autowired
    private TokenService tokenService;

    @Override
    public List<Map<String, Object>> getHistoryInfoList(String procInstId) throws Exception
    {
        // 查询每任务节点历史办理情况
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(procInstId)
                .orderByHistoricTaskInstanceStartTime()
                .asc()
                .list();

        List<Map<String, Object>> records = new ArrayList<>();
        for (HistoricTaskInstance hti : list) {
            Map<String, Object> result = new HashMap<>();
            result.put("taskId", hti.getId()); // 任务ID
            result.put("taskName", hti.getName()); // 任务名称
            result.put("taskKey", hti.getTaskDefinitionKey()); // 任务key
            result.put("processInstanceId", hti.getProcessInstanceId()); //流程实例ID
            result.put("startTime", DateUtils.format(hti.getStartTime())); // 开始时间
            result.put("endTime", DateUtils.format(hti.getEndTime())); // 结束时间
            result.put("status", hti.getEndTime() == null ? "待处理": "已处理"); // 状态
            result.put("assignee", StringUtils.isEmpty(hti.getAssignee())?"":UserCacheUtils.getValue(Long.valueOf(hti.getAssignee()))); // 办理人
            result.put("assigneeId", hti.getAssignee());
            // 撤回原因
            String message = hti.getDeleteReason();
            if(StringUtils.isEmpty(message)) {
                List<Comment> taskComments = taskService.getTaskComments(hti.getId());
                message = taskComments.stream()
                        .map(m -> m.getFullMessage()).collect(Collectors.joining("。"));
            }
            result.put("message", message);

            records.add(result);
        }

        return records;
    }

    @Override
    public void getHistoryProcessImage(String prodInstId, HttpServletResponse response) {
        InputStream inputStream = null;
        try {
            // 1.查询流程实例历史数据
            HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(prodInstId).singleResult();

            // 2. 查询流程中已执行的节点，按时开始时间降序排列
            List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(prodInstId)
                    .orderByHistoricActivityInstanceStartTime().desc()
                    .list();

            // 3. 单独的提取高亮节点id ( 绿色）
            List<String> highLightedActivityIdList =
                    historicActivityInstanceList.stream()
                        .map(HistoricActivityInstance::getActivityId).collect(Collectors.toList());

            // 4. 正在执行的节点 （红色）
            List<Execution> runningActivityInstanceList = runtimeService.createExecutionQuery()
                    .processInstanceId(prodInstId).list();

            List<String> runningActivityIdList = new ArrayList<>();
            for (Execution execution : runningActivityInstanceList) {
                if(StringUtils.isNotEmpty(execution.getActivityId())) {
                    runningActivityIdList.add(execution.getActivityId());
                }
            }

            // 获取流程定义Model对象
            BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());

            // 实例化流程图生成器
            CustomProcessDiagramGenerator generator = new CustomProcessDiagramGenerator();
            // 获取高亮连线id
            List<String> highLightedFlows = generator.getHighLightedFlows(bpmnModel, historicActivityInstanceList);
            // 生成历史流程图
            inputStream = generator.generateDiagramCustom(bpmnModel, highLightedActivityIdList,
                    runningActivityIdList, highLightedFlows,
                    "宋体", "微软雅黑", "黑体");

            // 响应相关图片
            response.setContentType("image/svg+xml");
            byte[] bytes = IOUtils.toByteArray(inputStream);
            ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write(bytes);
            outputStream.flush();
            outputStream.close();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            if( inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    @Transactional
    @Override
    public void cancel(String businessKey, String businessType,String procInstId, String message)
    {
        // 1. 删除当前流程实例

        runtimeService.deleteProcessInstance(procInstId,
                SecurityUtils.getUsername() + " 主动撤回了当前申请：" + message);
        // 2. 删除历史记录
        historyService.deleteHistoricProcessInstance(procInstId);
        historyService.deleteHistoricTaskInstance(procInstId);

    }
    @Override
    public Map<String, Object> getProcInstFinish(TaskFinshDTO taskFinshDTO)
    {

        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery()
//                .finished() // 已结束的
                .involvedUser(SecurityUtils.getUserId()+"") //代办涉及到的用户
                //.startedBy(SecurityUtils.getUserId()+"") //过滤当前登录人发起的流程 代办
                .orderByProcessInstanceStartTime().desc();

        // 获取当前的用户
        LoginUser loginUser = tokenService.getLoginUser();
        if (com.spic.common.core.utils.StringUtils.isNotNull(loginUser))
        {
            SysUser currentUser = loginUser.getSysUser();
            // 如果是超级管理员，则不过滤数据
            if (com.spic.common.core.utils.StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
            {
                query.involvedUser(currentUser.getUserId()+"");
            }
        }
        PageDomain pageDomain = TableSupport.getPageDomain();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        Integer begin = (pageNum -1) *  pageSize;
        List<Map<String, Object>> records = new ArrayList<>();
        if(StringUtils.isNotEmpty(taskFinshDTO.getProcessInstanceName())) {
            query.processInstanceNameLike("%" + taskFinshDTO.getProcessInstanceName() + "%");
        }

        long total = query.count();
        if(total==0){
            Map<String, Object> result = new HashMap<>();
            result.put("total", total);
            result.put("list", records);
            return result;
        }
        List<HistoricProcessInstance> historicProcessInstances = query.listPage(begin, pageSize);
        List<String> instancesIds = historicProcessInstances.stream().map(HistoricProcessInstance::getId).collect(Collectors.toList());
        List<BusinessStatus> statuses = businessStatusService.selectByInstanceId(instancesIds);
        Map<String, BusinessStatus> statusMap = statuses.stream().collect(Collectors.toMap(BusinessStatus::getProcessInstanceId, Function.identity()));

        for (HistoricProcessInstance pi : historicProcessInstances) {
            Map<String, Object> result = new HashMap<>();
            BusinessStatus status = statusMap.get(pi.getId());
            String businessType = "";
            if(status!= null){
                businessType = statusMap.get(pi.getId()).getBusinessType();
            }
            result.put("processInstanceId", pi.getId());
            result.put("businessType",businessType);
            result.put("processInstanceName", pi.getName());
            result.put("processKey", pi.getProcessDefinitionKey());
            result.put("version", pi.getProcessDefinitionVersion());
            try
            {
                result.put("proposer", UserCacheUtils.getValue(Long.valueOf(pi.getStartUserId())));
            } catch (Exception e)
            {
                e.printStackTrace();
            }
            result.put("businessKey", pi.getBusinessKey());
            result.put("startTime", DateUtils.format(pi.getStartTime()) );
            result.put("endTime", DateUtils.format(pi.getEndTime()) );
            // 原因
            result.put("deleteReason", pi.getDeleteReason());
             Integer s = status.getStatus();
            if(s!=null){
                if(s== BusinessStatusEnum.CANCEL.getCode()){
                    result.put("status", BusinessStatusEnum.CANCEL.getDesc());
                } else if(s== BusinessStatusEnum.WAIT.getCode()){
                    result.put("status", BusinessStatusEnum.WAIT.getDesc());
                }  else if(s== BusinessStatusEnum.PROCESS.getCode()){
                    result.put("status", BusinessStatusEnum.PROCESS.getDesc());
                }else if(s== BusinessStatusEnum.FINISH.getCode()){
                    result.put("status", BusinessStatusEnum.FINISH.getDesc());
                }
            }else{

            }
            records.add(result);
        }
        Map<String, Object> result = new HashMap<>();
        result.put("total", total);
        result.put("list", records);
        return result;
    }

    @Override
    public Map<String, Object> getProcInstRunning()
    {
        ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();
        PageDomain pageDomain = TableSupport.getPageDomain();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        Integer begin = (pageNum -1) *  pageSize;
        List<ProcessInstance> instanceList = query.listPage(begin,pageSize);
        long total = query.count();

        List<Map<String, Object>> records = new ArrayList<>();
        for (ProcessInstance pi : instanceList) {
            Map<String, Object> result = new HashMap<>();
            result.put("processInstanceId", pi.getProcessInstanceId());
            result.put("processInstanceName", pi.getName());
            result.put("processKey", pi.getProcessDefinitionKey());
            result.put("version", pi.getProcessDefinitionVersion());
            result.put("proposer", pi.getStartUserId());
            result.put("processStatus", pi.isSuspended() ? "已暂停": "已启动");
            result.put("businessKey", pi.getBusinessKey());
            result.put("startTime", DateUtils.format(pi.getStartTime()) );

            // 查询当前实例的当前任务
            List<Task> taskList = taskService.createTaskQuery()
                    .processInstanceId(pi.getProcessInstanceId()).list();
            String currTaskInfo = ""; // 当前任务
            for (Task task : taskList) {
                currTaskInfo += "任务名【" + task.getName() + "】，办理人【" + task.getAssignee() + "】<br>";
            }
            result.put("currTaskInfo", currTaskInfo);

            records.add(result);
        }
        Collections.sort(records, new Comparator<Map<String, Object>>() {
            @Override
            public int compare(Map<String, Object> m1, Map<String, Object> m2) {
                String date1 = (String)m1.get("startTime");
                String date2 = (String)m2.get("startTime");
                return date2.compareTo(date1);
            }
        });
        Map<String, Object> result = new HashMap<>();
        result.put("total", total);
        result.put("list", records);
        return result;
    }
}
